///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef __DataStream_H__
#define __DataStream_H__

#include "OgrePrerequisites.h"
#include "OgreString.h"
#include "OgreSharedPtr.h"
#include <istream>

namespace Ogre {
	
	
	template <size_t cacheSize>
	class StaticCache
	{
	protected:
		/// Static buffer
		char mBuffer[cacheSize];
		
		/// Number of bytes valid in cache (written from the beginning of static buffer)
		size_t mValidBytes;
		/// Current read position
		size_t mPos;
		
	public:
		/// Constructor
		StaticCache()
		{
			mValidBytes = 0;
			mPos = 0;
		}
		
		
		size_t cacheData(const void* buf, size_t count)
		{
			assert(avail() == 0 && "It is assumed that you cache data only after you have read everything.");
			
			if (count < cacheSize)
			{
				// number of bytes written is less than total size of cache
				if (count + mValidBytes <= cacheSize)
				{
					// just append
					memcpy(mBuffer + mValidBytes, buf, count);
					mValidBytes += count;
				}
				else
				{
					size_t begOff = count - (cacheSize - mValidBytes);
					// override old cache content in the beginning
					memmove(mBuffer, mBuffer + begOff, mValidBytes - begOff);
					// append new data
					memcpy(mBuffer + cacheSize - count, buf, count);
					mValidBytes = cacheSize;
				}
				mPos = mValidBytes;
				return count;
			}
			else
			{
				// discard all
				memcpy(mBuffer, (const char*)buf + count - cacheSize, cacheSize);
				mValidBytes = mPos = cacheSize;
				return cacheSize;
			}
		}
		
		size_t read(void* buf, size_t count)
		{
			size_t rb = avail();
			rb = (rb < count) ? rb : count;
			memcpy(buf, mBuffer + mPos, rb);
			mPos += rb;
			return rb;
		}
		
		
		bool rewind(size_t count)
		{
			if (mPos < count)
			{
				clear();
				return false;
			}
			else
			{
				mPos -= count;
				return true;
			}
		}
		
		bool ff(size_t count)
		{
			if (avail() < count)
			{
				clear();
				return false;
			}
			else
			{
				mPos += count;
				return true;
			}
		}
		
		
		size_t avail() const
		{
			return mValidBytes - mPos;
		}
		
		
		void clear()
		{
			mValidBytes = 0;
			mPos = 0;
		}
	};
	
	
	
	

	
	class _OgreExport DataStream : public MemAlloc_General
	{
	public:
		enum AccessMode
		{
			READ = 1, 
			WRITE = 2
		};
	protected:
		/// The name (e.g. resource name) that can be used to identify the source fot his data (optional)
		String mName;		
        /// Size of the data in the stream (may be 0 if size cannot be determined)
        size_t mSize;
		/// What type of access is allowed (AccessMode)
		uint16 mAccess;

        #define OGRE_STREAM_TEMP_SIZE 128
	public:
		/// Constructor for creating unnamed streams
        DataStream(uint16 accessMode = READ) : mSize(0), mAccess(accessMode) {}
		/// Constructor for creating named streams
		DataStream(const String& name, uint16 accessMode = READ) 
			: mName(name), mSize(0), mAccess(accessMode) {}
		/// Returns the name of the stream, if it has one.
		const String& getName(void) { return mName; }
		/// Gets the access mode of the stream
		uint16 getAccessMode() const { return mAccess; }
		
		virtual bool isReadable() const { return (mAccess & READ) != 0; }
		
		virtual bool isWriteable() const { return (mAccess & WRITE) != 0; }
        virtual ~DataStream() {}
		// Streaming operators
        template<typename T> DataStream& operator>>(T& val);
		
		virtual size_t read(void* buf, size_t count) = 0;
		
		virtual size_t write(const void* buf, size_t count)
		{
                        (void)buf;
                        (void)count;
			// default to not supported
			return 0;
		}

		
		virtual size_t readLine(char* buf, size_t maxCount, const String& delim = "\n");
		
	    
	    virtual String getLine( bool trimAfter = true );

	    
	    virtual String getAsString(void);

		
		virtual size_t skipLine(const String& delim = "\n");

		
		virtual void skip(long count) = 0;
	
		
	    virtual void seek( size_t pos ) = 0;
		
		
	    virtual size_t tell(void) const = 0;

		
	    virtual bool eof(void) const = 0;

		
        size_t size(void) const { return mSize; }

        
        virtual void close(void) = 0;
		

	};

	
	typedef SharedPtr<DataStream> DataStreamPtr;

	/// List of DataStream items
	typedef list<DataStreamPtr>::type DataStreamList;
	/// Shared pointer to list of DataStream items
	typedef SharedPtr<DataStreamList> DataStreamListPtr;

	
	class _OgreExport MemoryDataStream : public DataStream
	{
	protected:
        /// Pointer to the start of the data area
	    uchar* mData;
        /// Pointer to the current position in the memory
	    uchar* mPos;
        /// Pointer to the end of the memory
	    uchar* mEnd;
        /// Do we delete the memory on close
		bool mFreeOnClose;			
	public:
		
		
		MemoryDataStream(void* pMem, size_t size, bool freeOnClose = false, bool readOnly = false);
		
		
		MemoryDataStream(const String& name, void* pMem, size_t size, 
				bool freeOnClose = false, bool readOnly = false);

		
		MemoryDataStream(DataStream& sourceStream, 
				bool freeOnClose = true, bool readOnly = false);
		
		
		MemoryDataStream(DataStreamPtr& sourceStream, 
				bool freeOnClose = true, bool readOnly = false);

		
		MemoryDataStream(const String& name, DataStream& sourceStream, 
				bool freeOnClose = true, bool readOnly = false);

        
        MemoryDataStream(const String& name, const DataStreamPtr& sourceStream, 
            bool freeOnClose = true, bool readOnly = false);

        
		MemoryDataStream(size_t size, bool freeOnClose = true, bool readOnly = false);
		
		MemoryDataStream(const String& name, size_t size, 
				bool freeOnClose = true, bool readOnly = false);

		~MemoryDataStream();

		
		uchar* getPtr(void) { return mData; }
		
		
		uchar* getCurrentPtr(void) { return mPos; }
		
		
		size_t read(void* buf, size_t count);

		
		size_t write(const void* buf, size_t count);

		
		size_t readLine(char* buf, size_t maxCount, const String& delim = "\n");
		
		
		size_t skipLine(const String& delim = "\n");

		
		void skip(long count);
	
		
	    void seek( size_t pos );
		
		
	    size_t tell(void) const;

		
	    bool eof(void) const;

        
        void close(void);

		
		void setFreeOnClose(bool free) { mFreeOnClose = free; }
	};

    
    typedef SharedPtr<MemoryDataStream> MemoryDataStreamPtr;

    
	class _OgreExport FileStreamDataStream : public DataStream
	{
	protected:
		/// Reference to source stream (read)
		std::istream* mInStream;
		/// Reference to source file stream (read-only)
		std::ifstream* mFStreamRO;
		/// Reference to source file stream (read-write)
		std::fstream* mFStream;
        bool mFreeOnClose;	

		void determineAccess();
	public:
		
		FileStreamDataStream(std::ifstream* s, 
            bool freeOnClose = true);
		
		FileStreamDataStream(std::fstream* s, 
			bool freeOnClose = true);

		
		FileStreamDataStream(const String& name, 
            std::ifstream* s, 
            bool freeOnClose = true);

		
		FileStreamDataStream(const String& name, 
			std::fstream* s, 
			bool freeOnClose = true);

		
		FileStreamDataStream(const String& name, 
            std::ifstream* s, 
            size_t size, 
            bool freeOnClose = true);

		
		FileStreamDataStream(const String& name, 
			std::fstream* s, 
			size_t size, 
			bool freeOnClose = true);

		~FileStreamDataStream();

		
		size_t read(void* buf, size_t count);

		
		size_t write(const void* buf, size_t count);

		
        size_t readLine(char* buf, size_t maxCount, const String& delim = "\n");
		
		
		void skip(long count);
	
		
	    void seek( size_t pos );

		
		size_t tell(void) const;

		
	    bool eof(void) const;

        
        void close(void);
		
		
	};

	
	class _OgreExport FileHandleDataStream : public DataStream
	{
	protected:
		FILE* mFileHandle;
	public:
		/// Create stream from a C file handle
		FileHandleDataStream(FILE* handle, uint16 accessMode = READ);
		/// Create named stream from a C file handle
		FileHandleDataStream(const String& name, FILE* handle, uint16 accessMode = READ);
        ~FileHandleDataStream();

		
		size_t read(void* buf, size_t count);

		
		size_t write(const void* buf, size_t count);

		
		void skip(long count);
	
		
	    void seek( size_t pos );

		
		size_t tell(void) const;

		
	    bool eof(void) const;

        
        void close(void);

	};
	
	
}
#endif

